﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Data.Linq;
using System.Diagnostics;

namespace SurveySystem.Core
{
    public static class GroupsBLL
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="groupName"></param>
        /// <param name="ownerUserName"></param>
        /// <returns>false if group with this name exists</returns>
        public static bool CreateGroup(string groupName, string ownerUserName)
        {
            SurveySystemDataContext dc;
            User user;
            DCUSR(ownerUserName, out dc, out user);

            if (dc.Groups.Any(g => g.name.ToLower() == groupName.ToLower()))
                return false;

            Group group = new Group()
            {
                name = groupName
            };
            dc.Groups.InsertOnSubmit(group);

            Member member = new Member()
            {
                role = GroupMemberRole.Owner
            };

            user.Members.Add(member);
            group.Members.Add(member);


            dc.SubmitChanges();
            return true;
        }


        public static IEnumerable<GroupOfUser> ListGroups(string userName, bool onlyMember, string filter)
        {
            SurveySystemDataContext dc;
            User user;
            DCUSR(userName, out dc, out user);
            filter = string.IsNullOrEmpty(filter) ? "" : filter;

            IEnumerable<GroupOfUser> result = from m in user.Members
                                              select new GroupOfUser(m);
            if (!onlyMember)
            {
                var ugroups = from m in dc.Members
                              where m.User.name == userName
                              select m.Group;
                var joinableGroups = dc.Groups.Except(ugroups).Select(g => new GroupOfUser(g));
                result = result.Union(joinableGroups);
            }
            return result.Where(gou => gou.GroupName.ToLower().Contains(filter.ToLower()));
        }

        public static IEnumerable<GroupOfUser> ListGroupsPaged(string userName, bool onlyMember, string filter,
             int maximumRows, int startRowIndex)
        {
            return ListGroups(userName, onlyMember, filter).Skip(startRowIndex).Take(maximumRows);
        }

        public static int ListGroupsCount(string userName, bool onlyMember, string filter)
        {
            return ListGroups(userName, onlyMember, filter).Count();
        }

        private static void DCUSR(string userName, out SurveySystemDataContext dc, out User user)
        {
            dc = CommonUtils.GetDc();
            user = dc.Users.FirstOrDefault(u => u.name == userName);
            if (user == null)
                throw new Exception("No such user!");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="groupId"></param>
        /// <returns>false if user already sent a request</returns>
        public static bool JoinRequest(string userName, int groupId)
        {
            SurveySystemDataContext dc;
            User user;
            DCUSR(userName, out dc, out user);

            Group group = dc.Groups.FirstOrDefault(g => g.id == groupId);
            if (group == null)
                throw new Exception("groupId does not exist!");

            Member mem = dc.Members.FirstOrDefault(m => m.groupId == groupId && m.userId == user.id);
            if (mem != null)
            {
                if (mem.role == GroupMemberRole.Requesting)
                    return false;
                else
                    throw new Exception("Already member!");
            }

            Member member = new Member() { role = GroupMemberRole.Requesting };
            user.Members.Add(member);
            group.Members.Add(member);
            dc.SubmitChanges();
            return true;
        }

        public static GroupMemberRole GetRole(string userName, int groupId)
        {
            using (SurveySystemDataContext dc = CommonUtils.GetDc())
            {
                GroupMemberRole role = (from m in dc.Members
                                        where m.groupId == groupId && m.User.name == userName
                                        select m.role).FirstOrDefault();
                
                return role;
            }
        }

        public static Group GetGroup(int groupId)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();
            return (from g in dc.Groups
                    where g.id == groupId
                    select g).First();
        }

        public static IEnumerable<Member> ListMembers(int groupId)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();

            return from m in dc.Members
                   where m.groupId == groupId && m.role != GroupMemberRole.Requesting
                   select m;
        }

        public static IEnumerable<User> ListAdmins(int groupId)
        {
            var q = ListMembers(groupId).Where(m => m.role == GroupMemberRole.Admin).Select(m => m.User);
            Debug.Print(q.ToList().Count.ToString());
            return q;
        }

        public static IEnumerable<Member> ListMembersPaged(int groupId,
             int maximumRows, int startRowIndex)
        {
            return ListMembers(groupId).Skip(startRowIndex).Take(maximumRows);
        }

        public static int ListMembersCount(int groupId)
        {
            return ListMembers(groupId).Count();
        }

        public static IEnumerable<User> ListJoiningRequests(int groupId)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();
            return from m in dc.Members
                   where m.groupId == groupId && m.role == GroupMemberRole.Requesting
                   select m.User;
        }


        public static void HandleJoiningRequest(int groupId, string adminName, Guid userId, bool accept)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();

            if (dc.Users.FirstOrDefault(u => u.id == userId) == null)
                throw new Exception("No user with id 'userId'");
            User admin = dc.Users.FirstOrDefault(u => u.name == adminName);
            if (admin == null)
                throw new Exception("No user with name 'adminName'");
            
            Member member = (from m in dc.Members
                             where m.groupId == groupId && m.User.id == userId && m.role == GroupMemberRole.Requesting
                             select m).FirstOrDefault();
            if (member == null)
                throw new Exception("User has no joining request on the given group!");

            var adminGroups = from m in admin.Members
                              where m.role == GroupMemberRole.Owner || m.role == GroupMemberRole.Admin
                              select m.Group;
            if (!adminGroups.Contains(member.Group))
                throw new Exception("The given admin is not admin of this group!");

            if (accept)
            {
                member.role = GroupMemberRole.Member;
            }
            else
            {
                dc.Members.DeleteOnSubmit(member);
            }

            CommonUtils.SubmitAndHandleConflicts(dc);
        }

        public static void GrantAdminship(int groupId, string ownerName, Guid userId, bool grant)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();

            if (dc.Users.FirstOrDefault(u => u.id == userId) == null)
                throw new Exception("No user with id 'userId'");
            User owner = dc.Users.FirstOrDefault(u => u.name == ownerName);
            if (owner == null)
                throw new Exception("No user with name 'adminName'");

            Member ownerMembership = (from m in dc.Members
                                      where m.groupId == groupId && m.User.name == ownerName
                                      select m).FirstOrDefault();
            if (ownerMembership == null)
                throw new Exception("The given owner (ownerName) is not a member of the group!");
            if (ownerMembership.role != GroupMemberRole.Owner)
                throw new Exception("The given owner (ownerName) is not the owner of the group!");

            Member userMembership = (from m in dc.Members
                                     where m.groupId == groupId && m.User.id == userId
                                     select m).FirstOrDefault();
            if (userMembership == null)
                throw new Exception("No such user (userId) in this group!");
            
            userMembership.role = grant ? GroupMemberRole.Admin : GroupMemberRole.Member;

            CommonUtils.SubmitAndHandleConflicts(dc);
        }

        public static void RemoveUser(int groupId, Guid userId)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();
            Member member = (from m in dc.Members
                             where m.userId == userId && m.groupId == groupId
                             select m).FirstOrDefault();
            if (member == null)
                throw new Exception("No such user in this group!");
            if (member.role == GroupMemberRole.Owner)
                throw new Exception("Can not remove an owner!");
            dc.Members.DeleteOnSubmit(member);
            dc.SubmitChanges();
        }

        public static void PromoteNewOwner(int groupId, string oldOwnerName, Guid newOwnerId)
        {
            SurveySystemDataContext dc = CommonUtils.GetDc();
            Member oldOwner = (from m in dc.Members
                                where m.User.name == oldOwnerName && m.groupId == groupId
                                select m).FirstOrDefault();
            if (oldOwner == null)
                throw new Exception("No such user (oldOwnerName) in this group!");
            if (oldOwner.role != GroupMemberRole.Owner)
                throw new Exception("Not an owner (oldOwnerName)!");
            if (oldOwner.userId == newOwnerId)
                throw new Exception("Old and new owners are the same!");

            Member newOwner = (from m in dc.Members
                                where m.userId == newOwnerId && m.groupId == groupId && m.role == GroupMemberRole.Admin
                                select m).FirstOrDefault();
            if (newOwner == null)
                throw new Exception("Not a member or not an admin: newOwnerId!");

            oldOwner.role = GroupMemberRole.Admin;
            newOwner.role = GroupMemberRole.Owner;

            dc.SubmitChanges();
        }
    }

    
}
